import FileUploader from '@/components/FileUploader';
import TemplateFileConfigForm from '@/pages/Template/Make/components/TemplateFileConfigForm';
import TemplateModelConfigForm from '@/pages/Template/Make/components/TemplateModelConfigForm';
import {
  downloadTemplateUsingPost,
  makeTemplateUsingPost,
} from '@/services/backend/templateController';
import {
  ProCard,
  ProFormInstance,
  ProFormRadio,
  ProFormText,
  ProFormTextArea,
  StepsForm,
} from '@ant-design/pro-components';
import { Button, message, Modal, Tree } from 'antd';
import type { UploadFile } from 'antd/es/upload/interface';
import { saveAs } from 'file-saver';
import JSZip from 'jszip';
import React, { useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';

const MakeTemplate: React.FC = () => {
  const formRef = useRef<ProFormInstance>();
  // 文件上传列表
  const [fileList, setFileList] = useState<UploadFile[]>([]);
  // 旧数据对象
  const [oldData, setOldData] = useState<API.TemplateMakeRequest>();
  // 基本信息表单数据
  const [basicInfo, setBasicInfo] = useState<API.TemplateMakeRequest>();
  // 模板文件配置表单数据
  const [fileConfig, setFileConfig] = useState<API.TemplateMakerFileConfig>();
  // 模板模型配置表单数据
  const [modelConfig, setModelConfig] = useState<API.TemplateMakerModelConfig>();
  // 用来跳转页面
  const navigate = useNavigate();
  // 原始模板文件路径
  const [originProjectPath, setOriginProjectPath] = useState<string>();
  const [showSuccessModal, setShowSuccessModal] = useState(false);
  const [id, setId] = useState<string>();
  const [fileStructure, setFileStructure] = useState<any[]>([]);
  // 初始化时根据 id 是否存在决定弹窗状态
  const [open, setOpen] = useState(!id);
  // 用于强制重新渲染表单
  const [formKey, setFormKey] = useState(Date.now());
  const [zipBlob, setZipBlob] = useState<Blob>();
  const [selectedFileContent, setSelectedFileContent] = useState<string>('');
  const [selectedFilePath, setSelectedFilePath] = useState<string>('');
  // 用于是否正在加载中
  const [submitting, setSubmitting] = useState<boolean>(false);


  // 解析URL获取ID的函数
  const extractTemplateId = (url: string) => {
    const parts = url.split('/');
    return parts[parts.length - 2];
  };

  // 解析ZIP文件结构的函数
  const parseZipStructure = async (blob: Blob) => {
    try {
      const zip = new JSZip();
      await zip.loadAsync(blob);

      const structure: any[] = [];
      zip.forEach((relativePath, file) => {
        const pathParts = relativePath.split('/').filter((p) => p);
        let currentLevel = structure;

        pathParts.forEach((part, index) => {
          const existingNode = currentLevel.find((n) => n.title === part);
          const isLeaf = index === pathParts.length - 1 && !file.dir;

          if (existingNode) {
            currentLevel = existingNode.children || (existingNode.children = []);
          } else {
            const newNode = {
              title: part,
              key: pathParts.slice(0, index + 1).join('/'),
              isLeaf,
              children: [],
            };
            currentLevel.push(newNode);
            currentLevel = newNode.children;
          }
        });
      });

      return structure;
    } catch (error) {
      console.error('ZIP解析失败:', error);
      return [];
    }
  };

  // 当检测到 id 存在时关闭弹窗
  useEffect(() => {
    if (id) setOpen(false);
  }, [id, navigate]);

  // 弹窗处理文件变化
  const handleChange = (newFileList: UploadFile[]) => {
    if (newFileList.length > 0) {
      const file = newFileList[0];

      // 文件类型校验
      const isArchive = /\.(zip|rar|7z|gz|tar)$/i.test(file.name);
      if (!isArchive) {
        message.error('仅支持 ZIP/RAR/7Z/GZ/TAR 格式的压缩文件!');
        setFileList([]);
        return;
      }

      // 文件大小校验（500MB）
      if ((file.size || 0) / 1024 / 1024 >= 500) {
        message.error('文件大小不能超过 500MB!');
        setFileList([]);
        return;
      }
    }
    setFileList(newFileList);
  };

  // 弹窗提交处理
  const handleSubmit = async () => {
    if (!fileList.length) {
      message.error('请先上传压缩文件');
      return;
    }

    try {
      // 获取自定义上传组件响应值，因为每个文件都有一个响应值，自定义组件只能上传一个文件，所以只获取第一个文件的响应值
      const responseData = fileList[0].response;
      setOriginProjectPath(responseData);
      message.success('原始项目上传成功！');
      setOpen(false);
    } catch (error) {
      message.error('操作失败，请稍后重试');
    }
  };

  /**
   * 表单提交
   *
   * @param values 获取所有分步表单中的数据
   */
  const doSubmit = async (values: API.TemplateMakeRequest) => {
    // 清空旧的文件结构和内容（防止渲染旧数据）
    setFileStructure([]);
    setSelectedFileContent('');
    setSelectedFilePath('');

    // 数据转换（防止对象无定义）
    values.fileConfig = values.fileConfig || {};
    values.modelConfig = values.modelConfig || {};

    try {
      // 正在加载
      setSubmitting(true);
      const res = await makeTemplateUsingPost({
        ...values,
        originProjectPath,
        id,
      });
      if (res.data) {
        const response = await fetch(res.data);
        const blob = await response.blob();

        // 清空旧的压缩包数据
        setZipBlob(undefined);
        // 解析新的压缩包结构
        const structure = await parseZipStructure(blob);

        setZipBlob(blob);
        // 设置文件结构
        setFileStructure(structure);
        // 解析URL，获取id，并设置id
        setId(extractTemplateId(res.data));
        // 展示弹窗
        setShowSuccessModal(true);
        message.success('制作成功');
      }
    } catch (e: any) {
      message.error('制作失败' + e.message);
    } finally {
      // 加载成功
      setSubmitting(false);
    }
  };

  // 文件节点选择函数
  const handleTreeSelect = async (selectedKeys: React.Key[], info: any) => {
    const node = info.node;
    if (node.isLeaf && zipBlob) {
      try {
        const path = node.key as string;
        setSelectedFilePath(path);

        const zip = new JSZip();
        await zip.loadAsync(zipBlob);
        const file = zip.file(path);

        if (file) {
          const isText = /\.(txt|java|xml|properties|json|yml|yaml|html|js|css|md|ftl)$/i.test(
            path,
          );
          if (isText) {
            const content = await file.async('text');
            setSelectedFileContent(content);
          } else {
            setSelectedFileContent('该文件类型不支持文本预览');
          }
        } else {
          setSelectedFileContent('文件不存在');
        }
      } catch (error) {
        setSelectedFileContent('文件读取失败');
      }
    }
  };

  /**
   * 调用下载模板接口，下载模板文件
   * @param values
   */
  const doDownload = async () => {
    try {
      const blob = await downloadTemplateUsingPost({ id }, { responseType: 'blob' });
      // 使用 file-saver 来保存文件
      saveAs(blob, 'template.zip');
      // 下载成功后跳转到主页
      navigate(`/`);
    } catch (error: any) {
      message.error('下载失败，' + error.message);
    }
  };

  // 渲染页面
  return (
    <ProCard title={id ? '再次制作项目模板' : '首次制作项目模板'}>
      {/*首次制作时，上传原始项目的弹窗*/}
      <Modal
        title="上传原始项目"
        open={open}
        onCancel={() => {
          setOpen(false);
          navigate(-1);
        }}
        footer={[
          <Button
            key="cancel"
            onClick={() => {
              setOpen(false);
              navigate(-1);
            }}
          >
            取消
          </Button>,
          <Button
            key="submit"
            type="primary"
            onClick={handleSubmit}
            disabled={!fileList.length || fileList[0]?.status !== 'done'}
          >
            上传
          </Button>,
        ]}
        destroyOnClose
      >
        <FileUploader
          biz="make_project_template"
          value={fileList}
          onChange={handleChange}
          accept=".zip,.rar,.7z,.gz,.tar"
          description="请上传原始项目压缩包，打包时不要添加最外层目录！单个文件不超过500MB"
        />
      </Modal>

      {/* 首次制作或者再次制作只加载元数据基础信息时，才渲染表单，顺利填充默认值 */}
      {(!id || oldData) && (
        <StepsForm<API.GeneratorAddRequest>
          // 使用key强制重新渲染
          key={formKey}
          formRef={formRef}
          formProps={{ initialValues: oldData }}
          onFinish={doSubmit}
          submitter={{
            submitButtonProps: {
              loading: submitting
            }
          }}
        >
          {/*基本信息表单*/}
          <StepsForm.StepForm
            name="base"
            title="基本信息"
            onFinish={async (values) => {
              setBasicInfo(values);
              return true;
            }}
          >
            <ProFormText
              name={['meta', 'name']}
              label="名称"
              placeholder="请输入名称"
              rules={[
                {
                  required: true,
                  message: '请输入名称',
                },
                {
                  pattern: /^[a-zA-Z]+(?:[a-zA-Z_-]*[a-zA-Z]+)*$/,
                  message: '名称只能包含英文、下划线和中划线，且不能以下划线或中划线开头或结尾',
                },
              ]}
            />
            <ProFormText
              name={['meta', 'basePackage']}
              label="基础包名"
              placeholder="请输入基础包名"
              rules={[
                {
                  required: true,
                  message: '请输入基础包名',
                },
                {
                  pattern: /^[a-zA-Z]+(?:\.[a-zA-Z]+)*$/,
                  message: '基础包名只能包含英文和点，且不能以点开头或结尾',
                },
              ]}
            />
            <ProFormTextArea name={['meta', 'description']} label="描述" placeholder="请输入描述" />
            <ProFormText name={['meta', 'author']} label="作者" placeholder="请输入作者" />
            <ProFormRadio.Group
              name={['outputConfig', 'removeGroupFileFromRoot']}
              label="是否从未分组文件中移除组内的同名文件（默认是）"
              options={[
                {
                  label: '是',
                  value: true,
                },
                {
                  label: '否',
                  value: false,
                },
              ]}
            />
            <ProFormText
              name={['meta', 'version']}
              label="版本号"
              placeholder="请输入版本号"
              rules={[
                {
                  required: true,
                  message: '请输入版本号',
                },
                {
                  pattern: /^[0-9]+(?:\.[0-9]+)*$/,
                  message: '名称只能包含数字和小数点，且不能以小数点开头或结尾',
                },
              ]}
            />
          </StepsForm.StepForm>
          {/*模板模型配置表单*/}
          <StepsForm.StepForm
            name="modelConfig"
            title="模板模型配置"
            onFinish={async (values) => {
              setModelConfig(values);
              return true;
            }}
          >
            <TemplateModelConfigForm formRef={formRef} />
          </StepsForm.StepForm>
          {/*模板文件配置表单*/}
          <StepsForm.StepForm
            name="fileConfig"
            title="模板文件配置"
            onFinish={async (values) => {
              setFileConfig(values);
              return true;
            }}
          >
            <TemplateFileConfigForm formRef={formRef} />
          </StepsForm.StepForm>
        </StepsForm>
      )}

      {/*制作成功后展示弹窗，弹窗内可以预览生成后的项目模板文件*/}
      <Modal
        title="模板制作成功"
        open={showSuccessModal}
        width={1500}
        footer={[
          <Button key="download" type="primary" onClick={doDownload}>
            下载模板
          </Button>,
          <Button
            key="remake"
            onClick={() => {
              // 清空所有文件相关状态（防止旧数据渲染）
              setFileStructure([]);
              setZipBlob(undefined);
              setSelectedFileContent('');
              setSelectedFilePath('');
              // 关闭制作成功显示的弹窗
              setShowSuccessModal(false);
              // 再次进入制作模板页面
              navigate(`/template/make`);
              // 设置旧数据
              setOldData({
                ...basicInfo,
                ...fileConfig,
                ...modelConfig,
              })
              // 更新key强制重新渲染
              setFormKey(Date.now());
            }}
          >
            再次制作
          </Button>,
        ]}
      >
        <div
          style={{
            display: 'flex',
            height: '65vh',
            backgroundColor: 'rgb(255,255,255)',
          }}
        >
          {/* 左侧文件树容器 */}
          <div
            style={{
              width: 400,
              borderRight: '1px solid #e8e8e8',
              overflow: 'hidden',
              display: 'flex',
              flexDirection: 'column',
            }}
          >
            {/*项目结构所在区域样式*/}
            <div
              style={{
                padding: '12px 16px',
                borderBottom: '1px solid #e8e8e8',
                backgroundColor: '#ffffff',
                fontSize: 14,
                fontWeight: 500,
              }}
            >
              项目模板结构
            </div>

            <div
              style={{
                flex: 1,
                overflow: 'auto',
                paddingRight: 8,
              }}
            >
              <Tree.DirectoryTree
                treeData={fileStructure}
                defaultExpandAll={false}
                selectedKeys={[selectedFilePath]}
                onSelect={handleTreeSelect}
                // 自定义节点样式
                className="custom-tree"
                style={{
                  width: 'calc(100% + 100px)', // 补偿滚动条宽度
                  padding: '8px 0',
                }}
                // 自定义节点渲染
                titleRender={(node) => (
                  <span
                    style={{
                      display: 'inline-flex',
                      alignItems: 'center',
                      width: '100%',
                      marginLeft: 4,
                      fontSize: 13,
                    }}
                  >
                    {node.title}
                  </span>
                )}
              />
            </div>
          </div>

          {/* 右侧预览区域 */}
          <div
            style={{
              flex: 1,
              display: 'flex',
              flexDirection: 'column',
              minWidth: 0,
            }}
          >
            <div
              style={{
                padding: '16px 24px',
                borderBottom: '1px solid #e8e8e8',
                backgroundColor: '#fafafa',
                fontSize: 14,
                fontWeight: 500,
                color: 'rgba(0,0,0,0.85)',
              }}
            >
              {selectedFilePath || '文件预览'}
            </div>
            <pre
              style={{
                flex: 1,
                margin: 0,
                padding: 24,
                overflow: 'auto',
                backgroundColor: '#fff',
                fontSize: 13,
                lineHeight: 1.6,
                fontFamily: 'SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace',
                whiteSpace: 'pre-wrap',
                wordBreak: 'break-word',
                color: selectedFileContent.startsWith('⚠️') ? '#ff4d4f' : '#262626',
                position: 'relative',
              }}
            >
              {selectedFileContent || (
                <div
                  style={{
                    position: 'absolute',
                    top: '50%',
                    left: '50%',
                    transform: 'translate(-50%, -50%)',
                    color: '#bfbfbf',
                    fontSize: 14,
                  }}
                >
                  选择文件查看内容
                </div>
              )}
            </pre>
          </div>
        </div>
      </Modal>

      <style>
        {`
        .custom-tree .ant-tree-node-content-wrapper {
          display: flex !important;
          align-items: center;
          width: 100% !important;
          transition: none !important;
        }

        .custom-tree .ant-tree-node-selected {
          background-color: #e6f7ff !important;
          box-shadow: inset 0 0 0 1px #91d5ff !important;
          width: calc(100% + 8px) !important;
          margin-left: -8px;
          padding-left: 8px !important;
        }
        `}
      </style>
    </ProCard>
  );
};

export default MakeTemplate;
